﻿using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Threading;

namespace Win32
{
    internal class HiPerfTimer
    {
        [DllImport("Kernel32.dll")]
        private static extern bool QueryPerformanceCounter(
            out long lpPerformanceCount);

        [DllImport("Kernel32.dll")]
        private static extern bool QueryPerformanceFrequency(
            out long lpFrequency);

        private long startTime, stopTime;
        private long freq;

        // Constructor
        public HiPerfTimer()
        {
            startTime = 0;
            stopTime = 0;

            if (QueryPerformanceFrequency(out freq) == false)
            {
                // high-performance counter not supported
                throw new Win32Exception();
            }
        }

        // Start the timer
        public void Start()
        {
            // lets do the waiting threads there work
            Thread.Sleep(0);

            QueryPerformanceCounter(out startTime);
        }

        // Stop the timer
        public void Stop()
        {
            QueryPerformanceCounter(out stopTime);
        }

        // Returns the duration of the timer (in seconds)
        public double Duration
        {
            get
            {
                return (double)(stopTime - startTime) / (double)freq;
            }
        }
    }
}

namespace AGB
{
    using System;
    using System.Collections.Generic;
    using System.Drawing;
    using System.Drawing.Imaging;
    using System.IO;
    using System.Reflection;
    using System.Runtime.InteropServices;
    using System.Runtime.Serialization.Formatters.Binary;
    using System.Xml;
    using System.Xml.Serialization;

    using System;
    using System.Runtime.InteropServices;
    using System.ComponentModel;
    using System.Threading;



    internal class FastBitmap
    {
        private Bitmap image;
        private BitmapData bitmapData;
        private int height;
        private int width;
        private byte[] rgbValues;
        bool locked = false;

        public int Height
        {
            get
            {
                return this.height;
            }
        }

        public int Width
        {
            get
            {
                return this.width;
            }
        }

        public FastBitmap(int x, int y)
        {
            width = x;
            height = y;
            image = new Bitmap(x, y);
        }

        public byte[] GetAllPixels()
        {
            return rgbValues;
        }

        public void SetAllPixels(byte[] pixels)
        {
            rgbValues = pixels;
        }

        public Color GetPixel(int x, int y)
        {
            int blue = rgbValues[(y * image.Width + x) * 4];
            int green = rgbValues[(y * image.Width + x) * 4 + 1];
            int red = rgbValues[(y * image.Width + x) * 4 + 2];

            return Color.FromArgb(red, green, blue);
        }

        public void SetPixel(int x, int y, Color cIn)
        {
            int index = (y * image.Width + x) * 4;
            rgbValues[index] = cIn.B;
            rgbValues[index + 1] = cIn.G;
            rgbValues[index + 2] = cIn.R;
        }

        public static implicit operator Image(FastBitmap bmp)
        {
            return bmp.image;
        }

        public static implicit operator Bitmap(FastBitmap bmp)
        {
            return bmp.image;
        }

        public void LockPixels()
        {
            LockPixels(new Rectangle(0, 0, image.Width, image.Height));
        }

        private void LockPixels(Rectangle area)
        {
            if (locked)
                return;

            locked = true;

            bitmapData = image.LockBits(area, ImageLockMode.ReadWrite, PixelFormat.Format32bppRgb);

            IntPtr ptr = bitmapData.Scan0;
            int stride = bitmapData.Stride;
            int numBytes = image.Width * image.Height * 4;

            rgbValues = new byte[numBytes];
            Marshal.Copy(ptr, rgbValues, 0, numBytes);
        }

        public void UnlockPixels()
        {
            if (!locked)
                return;

            locked = false;
            Marshal.Copy(rgbValues, 0, bitmapData.Scan0, image.Width * image.Height * 4);
            image.UnlockBits(bitmapData);
        }
    }

    public static class Util
    {
        public static byte[,] ConvertTwoDimensionalArray(byte[] _source, int _width, int _height)
        {
            byte[,] Destination = new byte[_width, _height];

            for (int i = 0; i < _height; i++)
            {
                for (int k = 0; k < _width; k++)
                {
                    Destination[k, i] = _source[i * _width + k];
                }
            }

            return Destination;
        }


        public static byte[] ConvertOneDimensionalArray(byte[,] _source)
        {
            int _width = _source.GetUpperBound(0) + 1;
            int _height = _source.GetUpperBound(1) + 1;

            byte[] Destination = new byte[_width * _height];

            for (int i = 0; i < Destination.Length; i++)
            {
                Destination[i] = _source[i % _width, i / _width];
            }
            return Destination;
        }

        public static string FileRead(string fileName)
        {
            FileStream f = new FileStream(fileName, FileMode.OpenOrCreate);
            StreamReader r = new StreamReader(f);

            string read = r.ReadToEnd();

            r.Close();
            f.Close();

            return read;
        }

        public static void FileWrite(string fileName, string text)
        {
            FileStream f = new FileStream(fileName, FileMode.Create);
            StreamWriter s = new StreamWriter(f);

            s.Write(text);
            s.Close();
            f.Close();
        }

        public static void FileAppend(string fileName, string text)
        {
            FileStream f = new FileStream(fileName, FileMode.Append);
            StreamWriter s = new StreamWriter(f);

            s.Write(text);
            s.Close();
            f.Close();
        }

        /// <summary>
        /// Saves to a .png file with path nodes connecting
        /// </summary>
        public static void DumpCollision(ushort[,] collisionData, int mapX, int mapY, List<Point> path, int width, int height, string fileName)
        {
            Bitmap bitmap = new Bitmap(width, height);
            Image image = Image.FromHbitmap(bitmap.GetHbitmap());

            Graphics graphics = Graphics.FromImage(image);

            graphics.FillRectangle(Brushes.LightBlue, 0, 0, width, height);

            for (int y = 0; y < height; y++)
            {
                for (int x = 0; x < width; x++)
                {
                    if ((collisionData[x, y] & 1) > 0)
                        graphics.DrawRectangle(new Pen(Brushes.Black), x, y, 1, 1);
                }
            }

            if (path != null)
            {
                // draw pathing stuff
                Point trailingNode = new Point(-1, -1);

                foreach (Point node in path)
                {
                    if (trailingNode.X != -1 && trailingNode.Y != -1)
                    {
                        graphics.DrawLine(
                            new Pen(Brushes.Red),
                            new Point(node.X - mapX + 2, node.Y - mapY + 2),
                            new Point(trailingNode.X - mapX + 2, trailingNode.Y - mapY + 4));
                    }

                    //graphics.DrawRectangle(new Pen(Brushes.Orange), node.X - mapX - 45, node.Y - mapY - 45, 90, 90);

                    graphics.DrawRectangle(new Pen(Brushes.Red), node.X - mapX, node.Y - mapY, 4, 4);
                    graphics.FillRectangle(Brushes.White, node.X - mapX + 1, node.Y - mapY + 1, 3, 3);

                    trailingNode = node;
                }
            }

            graphics.Save();
            image.Save(fileName + ".png");
        }

        /// <summary>
        /// Saves to a .png file
        /// </summary>
        public static void DumpCollision(ushort[,] collisionData, int width, int height, string fileName)
        {
            Bitmap bitmap = new Bitmap(width, height);
            Image image = Image.FromHbitmap(bitmap.GetHbitmap());

            Graphics graphics = Graphics.FromImage(image);

            graphics.FillRectangle(Brushes.LightBlue, 0, 0, width, height);

            for (int y = 0; y < height; y++)
            {
                for (int x = 0; x < width; x++)
                {

                    if (//!((collisionData[x, y] & 1) == 1) && // plr walk
                        //((collisionData[x, y] & 3) == 3) &&
                        //((collisionData[x, y] & 5) == 5) &&
                        //((collisionData[x, y] & 7) == 7) &&
                        //((collisionData[x, y] & 8) == 8) &&
                        //((collisionData[x, y] & 0x0A) == 0x0A) &&
                        //((collisionData[x, y] & 0x0C) == 0x0C) &&
                        //((collisionData[x, y] & 0x0E) == 0x0E) &&

                        //((collisionData[x, y] & 4) == 4) && // jumpin over
                        //((collisionData[x, y] & 6) == 6) &&

                        //((collisionData[x, y] & 2) == 2) && // light radius
                        ((collisionData[x, y] & 1) > 0))
                        graphics.DrawRectangle(new Pen(Brushes.Black), x, y, 1, 1);
                    //else
                    //    graphics.DrawRectangle(new Pen(Brushes.LightBlue), x, y, 1, 1);
                }
            }

            graphics.Save();
            image.Save(fileName + ".png");
        }

        public static void DumpCollision(UInt16[,] collisions, string fileName)
        {
            DumpCollision(collisions, collisions.GetUpperBound(0) + 1, collisions.GetUpperBound(1) + 1, fileName);
        }

        public static void DumpCollision(byte[,] collisionData, string fileName)
        {
            int width = collisionData.GetUpperBound(0) + 1;
            int height = collisionData.GetUpperBound(1) + 1;

            Bitmap bitmap = new Bitmap(width, height);
            Image image = Image.FromHbitmap(bitmap.GetHbitmap());

            Graphics graphics = Graphics.FromImage(image);

            graphics.FillRectangle(Brushes.Black, 0, 0, width, height);

            for (int y = 0; y < height; y++)
            {
                for (int x = 0; x < width; x++)
                {
                    if (collisionData[x, y] == 1)
                        graphics.DrawRectangle(new Pen(Brushes.White), x, y, 1, 1);
                }
            }

            graphics.Save();
            image.Save(fileName + ".png");
        }

        public static void DumpMap(Color[,] colorData, string fileName)
        {
            int width = colorData.GetUpperBound(0) + 1;
            int height = colorData.GetUpperBound(1) + 1;

            Bitmap bitmap = new Bitmap(width, height);
            Image image = Image.FromHbitmap(bitmap.GetHbitmap());

            Graphics graphics = Graphics.FromImage(image);

            graphics.FillRectangle(Brushes.Black, 0, 0, width, height);

            for (int y = 0; y < height; y++)
            {
                for (int x = 0; x < width; x++)
                {
                    graphics.DrawRectangle(new Pen(colorData[x, y]), x, y, 1, 1);
                }
            }

            graphics.Save();

            image.Save(fileName + ".png");
        }

        public static Image GetCollisionImage(byte[,] collisions)
        {
            int width = collisions.GetUpperBound(0) + 1;
            int height = collisions.GetUpperBound(1) + 1;

            FastBitmap bitmap = new FastBitmap(width, height);

            bitmap.LockPixels();

            for (int y = 0; y < height; y++)
            {
                for (int x = 0; x < width; x++)
                {
                    if (collisions[x, y] == 1)
                        bitmap.SetPixel(x, y, Color.White);

                    if (collisions[x, y] > 1)
                    {
                        bitmap.SetPixel(x, y, Color.Green);
                        bitmap.SetPixel(x + 1, y, Color.Green);
                        bitmap.SetPixel(x - 1, y, Color.Green);
                        bitmap.SetPixel(x, y + 1, Color.Green);
                    }
                }
            }

            bitmap.UnlockPixels();

            return bitmap;
        }

        public static byte[] Serialize(object o)
        {
            MemoryStream ms = new MemoryStream();
            BinaryFormatter bf1 = new BinaryFormatter();
            bf1.Serialize(ms, o);
            return ms.ToArray();
        }

        public static T Deserialize<T>(byte[] data, int offset, int length)
        {
            MemoryStream ms = new MemoryStream(data, offset, length);
            BinaryFormatter bf1 = new BinaryFormatter();
            ms.Position = 0;

            return (T)bf1.Deserialize(ms);
        }

        public static void XmlSerialize<T>(object o, string fileName)
        {
            XmlSerializer serializer = new XmlSerializer(typeof(T));

            TextWriter writer = new StreamWriter(fileName);
            serializer.Serialize(writer, o);
            writer.Close();
        }

        public static T XmlDeserialize<T>(string fileName)
        {
            TextReader reader = new StreamReader(fileName);

            XmlSerializer serializer = new XmlSerializer(typeof(T));

            Object o = serializer.Deserialize(reader);

            reader.Close();

            return (T)o;
        }

        private static Random Rand = new Random();
        /// <summary>
        /// Creates a random string that doesn't begin with a number
        /// </summary>
        /// <param name="min"></param>
        /// <param name="max"></param>
        /// <returns></returns>
        public static string RandomString(int min, int max)
        {
            return RandomString(min, max, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789");
        }
        public static string RandomString(int min, int max, string allowedChars)
        {
            string s = "";

            int length = Rand.Next(min, max);

            for (int i = 0; i < length; i++)
                s += allowedChars[Rand.Next(allowedChars.Length)];

            return s;
        }

        /// <summary>
        /// Gets all assemblies of the specified type in the specified directory
        /// </summary>
        /// <param name="directory"></param>
        /// <returns></returns>
        public static List<string> GetAssemblyFileNames<t>(string directory)
        {
            List<string> assemblyFileNames = new List<string>();

            string[] pluginFiles = Directory.GetFiles(directory, "*.DLL");

            foreach (string filePath in pluginFiles)
            {
                string fileName = Path.GetFileNameWithoutExtension(filePath);

                Type ObjType = null;

                try
                {
                    // load it
                    Assembly assembly;
                    assembly = Assembly.Load(fileName);
                    if (assembly != null)
                    {
                        Type[] types = assembly.GetTypes();

                        foreach (Type type in types)
                        {
                            if (type.BaseType == typeof(t))
                            {
                                ObjType = type;
                                break;
                            }
                        }
                    }
                }
                catch (BadImageFormatException)
                {
                }

                if (ObjType != null)
                    assemblyFileNames.Add(fileName);
            }

            return assemblyFileNames;
        }

        /// <summary>
        /// Returns a new instance of the specified assembly file name
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="fileName"></param>
        /// <returns></returns>
        public static T LoadAssembly<T>(string fileName)
        {
            Assembly assembly = Assembly.Load(fileName);

            Type[] types = assembly.GetTypes();

            foreach (Type type in types)
            {
                if (type.BaseType == typeof(T))
                    return (T)assembly.CreateInstance(type.ToString());
            }

            return default(T);
        }
    }
}